package com.hmall.clients.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.hmall.clients.ItemClient;
import com.hmall.clients.mapper.SearchMapper;
import com.hmall.clients.pojo.Search;
import com.hmall.common.doc.ItemDoc;
import com.hmall.common.dto.PageDTO;
import com.hmall.common.pojo.Item;
import com.hmall.clients.service.SearchService;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private RestHighLevelClient client;
    @Autowired
    private SearchMapper searchMapper;

    @Autowired
    private ItemClient itemClient;

    @Override
    public void saveList(List<Item> items) {

        try {
            List<ItemDoc>itemDocs=new ArrayList<>(items.size());
            BeanUtils.copyProperties(items,itemDocs);
            BulkRequest request = new BulkRequest("item");
            for (ItemDoc itemDoc : itemDocs) {
                String json = JSON.toJSONString(itemDoc);
                request.add(new IndexRequest("hotel").id(itemDoc.getId().toString()).source(json,XContentType.JSON));
        }
            client.bulk(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public List<String> getSuggest(String key) {
        try {
            SearchRequest request = new SearchRequest("item");
            request.source().suggest(new SuggestBuilder()
                    .addSuggestion("suggestion"
                    , SuggestBuilders.completionSuggestion("suggestion")
                                    .prefix(key)
                                    .skipDuplicates(true)
                                    .size(10)
                    ));
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);

            //解析数据
            Suggest suggest = response.getSuggest();
            CompletionSuggestion suggestion = suggest.getSuggestion("suggestion");
            List<CompletionSuggestion.Entry.Option> options = suggestion.getOptions();
            ArrayList<String> list = new ArrayList<>(options.size());
            for (CompletionSuggestion.Entry.Option option : options) {
                String text = option.getText().toString();
                list.add(text);
            }
            return list;
        } catch (IOException e) {
            throw new RuntimeException();
        }
    }

    @Override
    public Map<String, List<String>> filters(Search search) {
        try {
            SearchRequest request = new SearchRequest("item");
            //2.准备dsl
            //2.1query
            //2.2设置size
            request.source().size(0);
            //2.3聚合
            if (search.getBrand()!=null){
                request.source().query(QueryBuilders.termQuery("brand",search.getBrand()));
            }else{
                request.source().aggregation(AggregationBuilders
                        .terms("brandAgg")
                        .field("brand")
                        .size(10)
                );
            }
            if (search.getCategory()!=null){
                request.source().query(QueryBuilders.termQuery("category",search.getCategory()));
            }else {
                request.source().aggregation(AggregationBuilders
                        .terms("categoryAgg")
                        .field("category")
                        .size(10)
                );
            }
            Integer maxprice = search.getMaxprice();
            Integer minprice = search.getMinprice();
            if (maxprice != null && minprice !=null){
                request.source().query(QueryBuilders.rangeQuery("price").gt(minprice).lt(maxprice));
            }
            //4发出请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //5解析结果
            Map<String, List<String>> result = new HashMap<>();
            Aggregations aggregations = response.getAggregations();
            if (search.getBrand()==null){
                List<String> brandAgg = getAgg(aggregations, "brandAgg");
                result.put("brand",brandAgg);
            }
            if (search.getCategory()==null){
                List<String> categoryAgg = getAgg(aggregations, "categoryAgg");
                result.put("category",categoryAgg);
            }
            return result;
        } catch (IOException e) {
            throw new RuntimeException();
        }
    }

    @Override
    public PageDTO<ItemDoc> getlist(Search search) {
        String key = search.getKey();
        Integer page = search.getPage();
        Integer size = search.getSize();
        String sortBy = search.getSortBy();
        Integer maxprice = search.getMaxprice();
        Integer minprice = search.getMinprice();
        String category = search.getCategory();
        String brand = search.getBrand();

        try {
            SearchRequest request = new SearchRequest("item");
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            if (key!=null&&key!=""){
                boolQuery.must(QueryBuilders.matchQuery("all",key));
                request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
            }
            if (maxprice!=null&&minprice!=null){
                boolQuery.filter(QueryBuilders.rangeQuery("price").lte(maxprice).gte(minprice));
            }
            if (category!=null&&category!=""){
                boolQuery.filter(QueryBuilders.termQuery("all",category));
            }
            if (brand!=null&&brand!=""){
                boolQuery.filter(QueryBuilders.termQuery("all",brand));
            }
            request.source().query(boolQuery);
            if (page!=null&&size!=null){
                request.source().from((page-1)*size).size(size);
            }
            if ("default".equals(sortBy)){
            }else {
                request.source().sort(sortBy, SortOrder.DESC);
            }
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            long total = response.getHits().getTotalHits().value;
            PageDTO<ItemDoc> itemDocPageDTO = new PageDTO<>();
            itemDocPageDTO.setTotal(total);
            SearchHit[] hits = response.getHits().getHits();
            ArrayList<ItemDoc> list = new ArrayList<>();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                ItemDoc itemDoc = JSON.parseObject(sourceAsString, ItemDoc.class);
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                if (!CollectionUtils.isEmpty(highlightFields)) {
                    // 根据字段名获取高亮结果
                    HighlightField highlightField = highlightFields.get("name");
                    if (highlightField != null) {
                        // 获取高亮值
                        String name = highlightField.getFragments()[0].string();
                        // 覆盖非高亮结果
                        itemDoc.setName(name);
                    }
                }
                list.add(itemDoc);
            }
            itemDocPageDTO.setList(list);
            return itemDocPageDTO;
        } catch (IOException e) {
            throw new RuntimeException();
        }

    }

    @Override
    public void putaway(Item item) {
        try {
            ItemDoc itemDoc = new ItemDoc(item);
            String json = JSON.toJSONString(itemDoc);
            IndexRequest request = new IndexRequest("item").id(itemDoc.getId().toString());
            request.source(json,XContentType.JSON);
            client.index(request,RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException();
        }
    }

    @Override
    public void soldout(Item item) {
        try {
            DeleteRequest request = new DeleteRequest("item",item.getId().toString());
            client.delete(request,RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    private List<String> getAgg(Aggregations aggregations,String aggName) {
        Terms brandAgg = aggregations.get(aggName);
        List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
        ArrayList<String> list = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            String key = bucket.getKeyAsString();
            list.add(key);
        }
        return list;
    }

}
